WICONF.initPageBar = {
    visiblePages: 5,// 页码数量
    percount: 20,// 每页显示的数据条数
    toolTxts: ['首页', '上页', '下页', '末页']
};
/**
 * @widoc $.fn.initPageBar
 * @namespace comp
 * @des 分页：对象为 pageBar 容器 class="manu"
 * 组件配置，在使用组件前调用：
 <pre class="des-conf"> $.setWiConf('initPageBar',{
visiblePages:5,// 页码数量（奇数）
percount:20// 每页显示的数据条数
toolTxts:['首页','上页','下页','末页']
});</pre>
 * @type function
 * @demo initPageBar/demo 普通分页(后台分页)，后台返回数据为 {success:true,msg:'错误提示',data:{totalcount:100,retlist:[]}}
 * @demo initPageBar/demo_front 前台分页，后台返回数据为 {success:true,msg:'错误提示',data:[]}
 * @demo initPageBar/demo_cus 自定义分页条 dom
 * @param {object} opts 配置参数
 * @param {int=} opts.percount 每页显示的数据条数，默认为 20，若定义了 opts.perList，opts.percount 必须为其中之一，否则默认为 opts.perList[0]
 * @param {int=} opts.per <span class="t_gray80">请用 opts.percount 替代。</span>
 * @param {Array=} opts.perList 可选择的每页页码列表，若未定义则不可切换
 * @param {object=} opts.render 自定义 dom 结构配置对象。自定义事件绑定请参考 opts.callback.render。默认为:
 <pre class="des-conf">render:{
    left: ['total', 'per', 'go'],
    center: [],
    right: ['prev', 'page', 'next']
} </pre>
 * @param {Array=} opts.render.left 左侧 dom 结构定义
 * - opts.render.left[i] {string|function} 依次指定绘制的内容
 * 类型为 string 时，绘制组件内部默认的项，对应关系如下：
 * total: 共x页x条; per: perList+每页几条; go: 页码跳转;
 * prev: 首页+上一页; next: 下一页+末页; page: 页码主体
 * 类型为 function 时，function(oPageInApi, api){}，返回 string 作为绘制内容，绘制的元素中严禁使用 data-wi-* 属性
 * - oPageInApi {object} 当前的分页信息，包含 percount, totalpage, curpage, totalcount
 * - api {object} 初始化完成后返回的组件 api
 * @param {Array=} opts.render.right 右侧 dom 结构定义，同上
 * @param {Array=} opts.render.center 中侧 dom 结构定义，同上
 * @param {object=} opts.callback 自定义事件绑定。
 * @param {object=} opts.callback.render 对 opts.render 中通过 function 设置的自定义内容定义事件。
 <pre class="des-conf">render:{
    name:{// 对应自定义绘制内容中的元素上的 data-manu 属性值
        evtype:function(e){...} // evtype 为绑定的事件类型，如：clikc；e 为触发事件的 event 对象
    }
}</pre>
 * @param {int=} opts.autoPage 数据源变更后首次跳转的页码（从 1 开始），之后此属性会重设为 1，可以在重置数据源时重新传入。默认为 1
 * @param {Array=} opts.toolTxts 长度为4的数组，页码中首页、上页、下页、末页显示的内容，文本或dom字符串
 * @param {Array=} opts.data 前台分页时的数据源，优先级高于 opts.url
 * @param {string=} opts.url 后台分页时的请求 url。opts.data 定义时此属性失效
 * @param {object=} opts.args 后台分页时发送到 opts.url 的请求参数
 * @param {boolean=} opts.showloading 后台分页请求时是否显示加载动画，默认 false
 * @param {boolean=} opts.requestOpts 后台分页时，自定义的请求配置，其中 onSuccess,showloading,ajaxOpts.url,ajaxOpts.data 失效，请用其他属性实现
 * @param {function=} opts.afterFlip 每次翻页后的回调函数 function(datalist,pageObj,data)
 *   - datalist {Array} 请求页所有数据
 *   - pageObj {object} 分页对象
 *       pageObj.totalpage 总页数
 *       pageObj.curpage 当前页码（从1开始）
 *       pageObj.percount 每页数据条数
 *       pageObj.totalcount 总数据数
 *   - data {object} 后台分页时，每次分页请求到的后台数据的 data 部分；前台分页时，所有数据的集合
 * @return {object|undefined} obj 接口对象
 * @rtn {object} obj.page 获得分页信息
 *   - {int} obj.page.percount 每页条数
 *   - {int} obj.page.totalpage 总页数
 *   - {int} obj.page.curpage 当前页码
 *   - {int} obj.page.totalcount 总数据条数
 * @rtn {function} obj.fReset(cusOpts) 重设组件配置
 *   - {object} cusOpts 参考初始化参数。注意：不支持 <span class="t_gray80">per</span>，请用 percount 替代。
 * @rtn {function} obj.reset(cusOpts) <span class="t_gray80">请用 obj.fReset() 替代。</span>
 * @rtn {function} obj.goPage(i) 跳转到第 i 页，i 从 1 开始
 * @rtn {function} obj.fGetCurData() 获取当前页的数据
 * @rtn {function} obj.fGetData() 获取全部页的数据（后台分页时，与 fGetCurData() 的返回值相同）
 * @rtn {function} obj.destroy() 销毁。<span class="t_gray80">组件内部 $.fn.initGrid 依赖此方法，项目中不需使用。</span>
 */
$.fn.initPageBar = function(opts){
    var el = $(this), api,
        CONF = WICONF.initPageBar;// 全局配置
    opts = $.extend({
        //per: CONF.per,// 兼容旧版本，同 percount
        percount: CONF.percount,// 每页显示的数据条数
        perList: [],// 可选择的每页页码列表，若未定义则不可切换
        render: {// 自定义 dom 结构配置对象
            left: ['total', 'per', 'go'],
            center: [],
            right: ['prev', 'page', 'next']
        },
        //callback: {},// 自定义事件绑定
        autoPage: 1,// 数据源变更后首次跳转的页码（从 1 开始），之后此属性会重设为 1，可以在重置数据源时重新传入
        toolTxts: CONF.toolTxts,// 长度为4的数组，页码中首页、上页、下页、末页显示的内容，文本或dom字符串
        //data:[],// 前台分页时的数据源
        //url:'',// 后台分页时请求的 url
        //args: {},// 后台分页时的参数
        showloading: false,// 后台分页请求时是否显示加载动画
        requestOpts: {},// $.reuqest 时需要扩展的配置，其中 onSuccess,showloading,ajaxOpts.url,ajaxOpts.data 失效，请用其他属性实现
        afterFlip: $.noop// function(aCurData, oPageInApi, oriData) 转页后的回调函数。aCurData:当前页数据;oPageInApi:当前分页信息;oriData:前台分页为全部数据，后台分页为后台请求返回的原始数据
    }, opts);
    var aPerList, oPageInApi = {},
        nPer,// 每页显示的条数 - 数据处理前定义的临时值，若不能正常跳页，则不会作用到 oPageInApi
        aAllData, aCurData,// 全部数据（前台分页时才有意义），当前页数据 - 这两个变量需要与 dom 绘制同步
        oReqOpts, oReqAjaxOpts,// 请求时使用的用户自定义配置
        aLeftRender,// 左侧 dom 结构定义
        aRightRender,// 右侧 dom 结构定义
        aCenterRender,// 中间 dom 结构定义
        oRenderCb;// 对 opts.render 中自定义内容绑定的事件定义
    // 翻页按钮点击事件
    var fChangeGoClick = function(){
        fGoPage(el.find('.manu-txt').val());
    };
    // 页码点击事件
    var fChangePageClick = function(){
        fGoPage($(this).attr('data-page'));
    };
    // 切换 perList 选项时的回调
    var fOnPerListChange = function(){
        nPer = parseInt($(this).val(), 10);// 不应同步到 opts，修改 opts 应该通过 fReset 接口实现
        fGoPage(1);
    };
    // 获取当前页的数据
    var fGetCurData = function(){
        return aCurData;
    };
    // 获取全部页的数据（后台分页时，与 fGetCurData() 的返回值相同）
    var fGetData = function(){
        return aAllData || aCurData;// 后台分页时 aAllData 为 undefined，此时返回当前页数据
    };
    // 绘制分页条 dom，并触发用户回调
    var fDrawDom = function(){
        var nTotalcount = oPageInApi.totalcount,
            nTotalpage = oPageInApi.totalpage,
            nPercount = oPageInApi.percount,
            nCurpage = oPageInApi.curpage;
        var toolTxts = opts.toolTxts,// 首页、上页、下页、末页
            oDrawFunctions;// 储存默认绘制方法的变量
        var i, str = '';// 临时变量
        /* 组件中默认的 dom 结构绘制 */
        // 绘制共几页几条
        var fDrawTotalPage = function(){
            return '<div class="manu-item">共 ' + nTotalpage + ' 页 ' + nTotalcount + ' 条</div>';
        };
        // 绘制每页几条
        var fDrawPerList = function(){
            var len, str;
            if(nTotalcount){
                len = aPerList && aPerList.length;
                if(len){// 定义了每页显示条数列表
                    str = '<select class="manu-sel" data-wi-manu="per">';
                    for(var i = 0; i < len; i++){
                        str += '<option' + (aPerList[i] == nPercount ? ' selected' : '') + '>' + aPerList[i] + '</option>';
                    }
                    str += '</select>';
                }
                else{
                    str = nPercount;
                }
                return '<div class="manu-item">每页 ' + str + ' 条</div>';
            }
        };
        // 绘制跳转第几页
        var fDrawGoPage = function(){
            if(nTotalcount){
                return '<div class="manu-item">' +
                    '<input class="manu-txt" type="text"> 页 <input type="button" class="manu-btn" value="跳转" data-wi-manu="go">' +
                    '</div>';
            }
        };
        // 绘制首页、上一页
        var fDrawPrevious = function(){
            var str;
            if(nTotalcount){
                if(nCurpage == 1){
                    str = '<span class="manu-lnk manu-lnk-0">' + toolTxts[0] + '</span>' +
                        '<span class="manu-lnk">' + toolTxts[1] + '</span>';
                }
                else{
                    str = '<a href="javascript:void(0)" class="manu-lnk manu-lnk-0" data-page="1" data-wi-manu="page">' +
                        toolTxts[0] + '</a>' +
                        '<a href="javascript:void(0)" class="manu-lnk" data-page="' + (nCurpage - 1) + '" data-wi-manu="page">' +
                        toolTxts[1] + '</a>';
                }
                return '<div class="manu-item manu-pagebefore">' + str + '</div>';
            }
        };
        // 绘制下一页、末页
        var fDrawNext = function(){
            var str;
            if(nTotalcount){
                if(nCurpage >= nTotalpage){
                    str = '<span class="manu-lnk">' + toolTxts[2] + '</span>' +
                        '<span class="manu-lnk manu-lnk-x">' + toolTxts[3] + '</span>';
                }
                else{
                    str = '<a href="javascript:void(0)" class="manu-lnk" data-page="' + (nCurpage + 1) + '" data-wi-manu="page">' +
                        toolTxts[2] + '</a>' +
                        '<a href="javascript:void(0)" class="manu-lnk manu-lnk-x" data-page="' + nTotalpage + '" data-wi-manu="page">' +
                        toolTxts[3] + '</a>';
                }
                return '<div class="manu-item manu-pageafter">' + str + '</div>';
            }
        };
        // 绘制页码
        var fDrawPageNum = function(){
            var visiblePages,// 最多显示几个页码
                before, after,// 当前页码前、后显示的页码数
                pStart, pEnd;// 页码中的显示的第一页、最后一页
            var str = '';
            if(nTotalcount){
                // 预处理显示页码
                visiblePages = CONF.visiblePages;
                before = Math.floor(visiblePages / 2);
                after = visiblePages - before - 1;
                if(nCurpage - before > 0 && nCurpage + after <= nTotalpage){
                    pStart = nCurpage - before;
                    pEnd = nCurpage + after;
                }
                else if(nCurpage - before <= 0){
                    pStart = 1;
                    pEnd = Math.min(visiblePages, nTotalpage);
                }
                else{
                    pStart = Math.max(1, nTotalpage - visiblePages + 1);
                    pEnd = nTotalpage;
                }
                // dom 字符串
                for(var i = pStart; i <= pEnd; i++){
                    if(i == nCurpage){
                        str += '<span class="manu-lnk manu-lnk-i">' + i + '</span>';
                    }
                    else{
                        str += '<a class="manu-lnk" href="javascript:void(0)" data-page="' + i + '" data-wi-manu="page">' + i + '</a>';
                    }
                }
                return '<div class="manu-item manu-page">' + str + '</div>';
            }
        };
        // 根据内容排序数组，转换成对应字符串
        var fGetRenderStr = function(aRender){
            var len = aRender && aRender.length,
                str = '', sCus;
            if(len){// 根据内容排序数组，转换成对应字符串
                for(var i = 0, item; i < len; i++){
                    item = aRender[i];
                    if(typeof item == 'string'){
                        str += oDrawFunctions[item]() || '';
                    }
                    else{// 自定义内容中的 inline-block 的元素需要修改 vertical-align 才能垂直对齐
                        sCus = item(oPageInApi, api);
                        str += '<div class="manu-item manu-cus">' + sCus + '</div>';
                    }
                }
            }
            return str;
        };
        // 储存默认绘制方法的变量
        oDrawFunctions = {
            total: fDrawTotalPage,
            per: fDrawPerList,
            go: fDrawGoPage,
            prev: fDrawPrevious,
            next: fDrawNext,
            page: fDrawPageNum
        };
        str += '<div class="manu-l">' + fGetRenderStr(aLeftRender) + '</div>' +// manu-l
            '<div class="manu-r">' + fGetRenderStr(aRightRender) + '</div>' +// manu-r
            '<div class="manu-c">' + fGetRenderStr(aCenterRender) + '</div>';// manu-c
        el.html(str);
    };
    // toPage {number|string} 转到第 toPage(从1起) 页
    // bReset {boolean} 是否来自 fReset()，true 时，若 toPage 不合法将强制转至第1页
    var fGoPage = function(toPage, bReset){
        var aStatData = opts.data,// 前台分页数据源
            nStatCurIdx,// 前台分页临时参数：当前页第一条数据的索引
            nTotalcount, nTotalpage;// 全部数据条数，总页数
        // 数据处理
        var fDataHandler = function(oriData){
            // 绘制 dom 前将临时变量应用到 oPageInApi
            oPageInApi.totalcount = nTotalcount;
            oPageInApi.totalpage = nTotalpage;
            oPageInApi.curpage = toPage;
            oPageInApi.percount = nPer;
            fDrawDom();// 根据 oPageInApi 绘制 dom
            opts.afterFlip(aCurData, oPageInApi, oriData);// 转页后的回调函数
        };
        toPage = parseInt(toPage, 10);// 格式化为数字
        // 每页条数可能通过 reset 或"每页条数下拉框"修改，因此需要重算总页数
        if(aStatData){// 前台分页，可以直接获得数据总数
            nTotalcount = aStatData.length;
            nTotalpage = Math.ceil(nTotalcount / nPer);// 根据数据总数计算总页码
        }
        else if(!bReset){// 后台分页，非 reset，获取上次同源请求的数据总数，用于在请求前做初步判断
            nTotalcount = oPageInApi.totalcount;
            nTotalpage = Math.ceil(nTotalcount / nPer);// 根据数据总数计算总页码
        }
        // 判断 toPage 是否合法：reset 时不合法则改为 1，非 reset 时不合法则提示
        if(bReset){
            if(isNaN(toPage)
                || toPage < 1
                || nTotalpage && toPage > nTotalpage){
                toPage = 1;
            }
        }
        else{
            if(isNaN(toPage)){// 非数字
                $.showAlert('页码必须为数字!');
                return;
            }
            if(toPage < 1
                || nTotalpage && toPage > nTotalpage){
                $.showAlert('页码超过范围!');
                return;
            }
        }
        // 数据处理
        if(aStatData){// 静态数据
            nStatCurIdx = (toPage - 1) * nPer;
            // 配置同步全局变量
            aAllData = aStatData;
            aCurData = aAllData.slice(nStatCurIdx, Math.min(nTotalcount, nStatCurIdx + nPer));
            fDataHandler(aAllData);
        }
        else{
            $.request($.extend({}, oReqOpts, {
                onSuccess: function(data){
                    nTotalcount = data.totalcount;// 后台数据发生变化时可能与上次请求的数据总数不同
                    nTotalpage = Math.ceil(nTotalcount / nPer);// 重算总页码
                    // 页码超出数据范围
                    if(nTotalpage && toPage > nTotalpage){
                        //if(bReset){// autoPage 可能超出实际范围
                        //    fGoPage(1);
                        //    return;
                        //}
                        //else{// 非 reset，转页前后台数据源总页数减少
                        //    $.showConfirm('数据已更新，无法转到第' + toPage + '页，是否转到第1页?', function(){
                        //        fGoPage(1);
                        //    });
                        //    return;
                        //}
                        $.showMsg('页码超出范围，将跳转到第' + nTotalpage + '页!', function(){
                            fGoPage(nTotalpage);
                        })
                    }
                    // 配置同步全局变量
                    aAllData = undefined;
                    aCurData = data.retlist;
                    fDataHandler(data);
                },
                showloading: opts.showloading,
                ajaxOpts: $.extend({}, oReqAjaxOpts, {
                    url: opts.url,
                    data: $.extend({
                        curpage: toPage,// 从1开始
                        percount: nPer
                    }, opts.args)
                })
            }));
        }
    };
    // 销毁
    var fDestroy = function(){
        // 解绑组件默认事件
        el.off('change.wiManu')
          .off('click.wiManuGo')
          .off('click.wiManuPage');
        fUnbindCbEvent();// 解绑组件自定义事件
        el.remove();
        for(var key in api){
            delete api[key];
        }
    };
    // 事件解绑
    var fUnbindCbEvent = function(){
        var oCurRenderCb;// 循环时记录 oRenderCb 中的项
        if(oRenderCb){// 解绑自定义事件
            for(var name in oRenderCb){
                if(oRenderCb.hasOwnProperty(name)){
                    oCurRenderCb = oRenderCb[name];
                    for(var eType in oCurRenderCb){
                        if(oCurRenderCb.hasOwnProperty(eType)){
                            el.off(eType + '.wiManuCus_' + name);
                        }
                    }
                }
            }
        }
    };
    // 事件绑定
    var fBindCbEvent = function(){
        var oCurRenderCb;// 循环时记录 oRenderCb 中的项
        // 记录 oRenderCb
        oRenderCb = undefined;
        if(opts.callback){
            oRenderCb = opts.callback.render;
        }
        // 绑定自定义事件
        if(oRenderCb){
            for(var name in oRenderCb){
                if(oRenderCb.hasOwnProperty(name)){
                    oCurRenderCb = oRenderCb[name];
                    for(var eType in oCurRenderCb){
                        if(oCurRenderCb.hasOwnProperty(eType)){
                            el.on(eType + '.wiManuCus_' + name, '[data-manu="' + name + '"]', oCurRenderCb[eType]);
                        }
                    }
                }
            }
        }
    };
    var fResetRender = function(){
        var oRender = opts.render;// 自定义dom结构顺序，重置 opts.render 后只需修改对应全局变量，效果会在下次绘制时生效
        // 判断 opts.render 中各部分是否包含默认模块的定义
        var fRenderHasItem = function(str){
            return aLeftRender.indexOf(str) != -1 || aRightRender.indexOf(str) != -1 || aCenterRender.indexOf(str) != -1;
        };
        aLeftRender = oRender.left || [];// 左侧dom结构顺序 未定义则为 []
        aRightRender = oRender.right || [];// 右侧dom结构顺序 未定义则为 []
        aCenterRender = oRender.center || [];// 中间dom结构顺序 未定义则为 []
        // render 的变化可能需要引起默认事件改变
        if(fRenderHasItem('go')){
            el.off('click.wiManuGo')
              .on('click.wiManuGo', '[data-wi-manu="go"]', fChangeGoClick);// 翻页按钮点击事件
        }
        if(fRenderHasItem('page') || fRenderHasItem('prev') || fRenderHasItem('next')){
            el.off('click.wiManuPage')
              .on('click.wiManuPage', '[data-wi-manu="page"]', fChangePageClick);// 页码跳转事件
        }
        if(fRenderHasItem('per')){
            el.off('change.wiManu')
              .on('change.wiManu', '[data-wi-manu="per"]', fOnPerListChange);// 绑定 perList 下拉框改变事件
        }
    };

    // 重设配置
    var fReset = function(cusOpts){
        if(cusOpts){
            $.extend(opts, cusOpts);
            if(cusOpts.data){// 重设了 data，将 url 置空
                opts.url = undefined;
            }
            else if(cusOpts.url){// 重设了 url，将 data 置空
                opts.data = undefined;
            }
            if(cusOpts.render){
                fResetRender();
            }
            if(cusOpts.callback){// 重绑自定义事件
                fUnbindCbEvent();
                fBindCbEvent();
            }
        }
        else{// 初始化时，处理 opts
            if(opts.data){
                opts.url = undefined;
            }
            /* 旧版本兼容 begin */
            if(typeof opts.per != 'undefined'){// 旧版本 fReset 中不支持 per 的修改，此处不需做 cusOpts 的兼容
                opts.percount = opts.per;
                delete opts.per;
            }
            /* 旧版本兼容 end */
            fResetRender();
            fBindCbEvent();// 绑定自定义事件
        }
        /* 应用配置 */
        // 预先处理每页条数
        nPer = opts.percount;// 每页显示条数
        aPerList = opts.perList;
        if(aPerList.length && aPerList.indexOf(nPer) == -1){// 定义了每页显示条数列表，且 percount 不在 aPerList 中
            nPer = aPerList[0];
        }
        // 请求配置处理
        oReqOpts = $.extend({}, opts.requestOpts);// 其中除 ajaxOpts 外无有用的复杂类型，可以浅拷贝
        oReqAjaxOpts = $.extend({}, oReqOpts.ajaxOpts);// 记录 ajaxOpts 缓存时再处理
        delete oReqOpts.ajaxOpts;
        if(oReqAjaxOpts){
            delete oReqAjaxOpts.url;// 由 opts.url 定义
            delete oReqAjaxOpts.data;// 由 opts.args 定义
        }
        delete oReqOpts.showloading;// 由 opts.showloading 定义
        /* 数据绘制 */
        if(opts.data || opts.url){// 只在定义了数据源时绘制，用于支持初始化时不带入数据
            fGoPage(opts.autoPage, true);
            opts.autoPage = 1;// 数据源变更后首次跳转的页码（从 1 开始），之后此属性会重设为 1，可以在重置数据源时重新传入
        }
    };
    // 初始化
    var fInit = function(){
        el.addClass('manu clearf');
        fReset();
    };
    api = {
        page: oPageInApi,
        destroy: fDestroy,
        fReset: fReset,
        reset: fReset,// 兼容旧版本
        goPage: fGoPage,
        fGetCurData: fGetCurData,// 获取当前页的数据
        fGetData: fGetData// 获取全部页的数据（后台分页时，与 fGetCurData() 的返回值相同）
    };// 返回的接口对象
    fInit();
    return api;
};